import {
  AppStyleCollections,
  BodyAttrs,
  ELEMENT_HIDE_TYPE,
  InsertPosition,
  logger,
  pageJQuery,
} from '../../../types/constants'
import { SYMBOLS } from '../../../types/interfaces'
import { ClipboardUtils } from '../ui/clipboard'
import { ElementUtils } from '../ui/element'
import { StyleUtils } from '../ui/style'

export enum CopyButtonUpdateType {
  TextContent = 'textContent',
  DataAttribute = 'dataAttribute',
}

export interface CopyButtonUpdateOptions {
  duration?: number
  showPlaceholder?: boolean
  states?: {
    success?: {
      text: string
    }
    error?: {
      text: string
    }
    default?: {
      text: string
    }
  }
}

/**
 * DOM 操作工具类
 */
export class DomUtils {
  static get name(): string {
    return 'DomUtils'
  }

  /**
   * 设置元素样式属性（默认为隐藏样式）
   * @param target 目标元素
   * @param value 隐藏值，默认 'none'
   * @param attributeName 样式属性名，默认 'display'
   * @param useImportant 是否使用 !important 声明
   */
  static setElementCssAttribute(
    target: JQuery<HTMLElement> | HTMLElement | HTMLElement[] | undefined,
    value: string = 'none',
    attributeName: string = 'display',
    useImportant: boolean = true,
  ): void {
    const elements = this.elementToHtmlList(target)
    elements.forEach((element) => {
      if (element && element.style) {
        element.style.setProperty(attributeName, value, useImportant ? 'important' : '')
      }
    })
  }

  /**
   * 设置元素 CSS 文本
   * @param target 目标元素
   * @param cssText CSS 文本
   */
  static setElementCssText(
    target: JQuery<HTMLElement> | HTMLElement | HTMLElement[] | undefined,
    cssText: string,
  ): void {
    const elements = this.elementToHtmlList(target)
    elements.forEach((element) => {
      if (element && element.style) {
        element.style.cssText = cssText
      }
    })
  }

  /**
   * 将元素转换为 HTML 元素列表
   * @param target 目标元素
   * @returns HTML 元素列表
   */
  private static elementToHtmlList(
    target: JQuery<HTMLElement> | HTMLElement | HTMLElement[] | undefined,
  ): HTMLElement[] {
    if (!target)
      return []

    return ElementUtils.isJQueryObject(target)
      ? target.toArray()
      : Array.isArray(target) ? target : [target]
  }

  /**
   * 控制元素显示与隐藏
   * @param show 是否显示
   * @param selector 选择器
   */
  static simpleToggleElement(show: boolean, selector: string): void {
    $(selector).toggleClass(AppStyleCollections.Hide.AD, !show)
    show ? $(selector).show() : $(selector).hide()
  }

  /**
   * 显示或隐藏元素
   * @param $element 元素
   * @param options 选项
   * @param options.show 是否显示
   * @param options.type 隐藏类型
   * @param options.softShow 是否软显示
   * @param options.softHide 是否软隐藏
   */
  static showControl(
    $element: JQuery<HTMLElement>,
    options: {
      show: boolean
      type?: string
      softShow?: boolean
      softHide?: boolean
    },
  ): void {
    const { show, type = ELEMENT_HIDE_TYPE.ENGINE, softShow = false, softHide = false } = options

    if (show) {
      // 显示时始终清除所有隐藏标记
      $element.removeClass(AppStyleCollections.Hide.AD)
        .removeAttr(BodyAttrs.HIDE_TYPE)

      if (!softShow && $element.length > 0) {
        $element.css('display', '').show()
      }
    }
    else {
      const $newElement = $element.not(`.${AppStyleCollections.Hide.AD}`)
      $newElement.addClass(AppStyleCollections.Hide.AD)
        .attr(BodyAttrs.HIDE_TYPE, type)

      if (!softHide && $newElement.length > 0) {
        this.setElementCssAttribute($newElement)
      }
    }
  }

  /**
   * 添加元素样式
   */
  static addStyle($element: JQuery<HTMLElement>, additionalStyle: string): void {
    if (!$element.length || !additionalStyle)
      return

    const originalStyle = $element.attr('style') || ''
    const mergedStyle = StyleUtils.mergeStyles(originalStyle, additionalStyle)

    $element.attr('style', mergedStyle)
  }

  /**
   * 插入元素
   * @param $target 目标元素
   * @param $newElement 新元素
   * @param position 插入位置
   */
  static insertElement(
    $target: JQuery<HTMLElement>,
    $newElement: JQuery<HTMLElement>,
    position?: InsertPosition,
  ): void {
    switch (position) {
      // 在目标元素外部前面插入
      case InsertPosition.Before:
        $target.before($newElement)
        break
      // 在目标元素外部后面插入
      case InsertPosition.After:
        $target.after($newElement)
        break
      // 在容器内部开头插入
      case InsertPosition.Prepend:
        $target.prepend($newElement)
        break
      // 在容器内部末尾插入
      case InsertPosition.Append:
        $target.append($newElement)
        break
      default:
        $target.append($newElement)
    }
  }

  /**
   * 触发元素的真实点击事件
   */
  static triggerRealClick($element: JQuery<HTMLElement>): boolean {
    try {
      // 使用页面 jQuery 查找元素并触发
      const pageElement = pageJQuery($element[0])
      pageElement.trigger('click')

      // 同时触发原生 click 确保覆盖
      $element[0].click()

      return true
    }
    catch (error) {
      console.error('触发点击失败:', error)
      return false
    }
  }

  /**
   * 处理 javascript 伪协议链接
   */
  static handleJavascriptLink($element: JQuery): void {
    $element[0].click()
  }

  /**
   * 更新复制按钮状态
   */
  static updateCopyButtonStatus(
    $button: JQuery<HTMLElement>,
    updateType: CopyButtonUpdateType = CopyButtonUpdateType.TextContent,
    status: 'success' | 'error' | 'default' = 'default',
    options?: CopyButtonUpdateOptions,
  ): void {
    const defaultStates = {
      success: {
        text: `${SYMBOLS.STATUS.SUCCESS}成功`,
      },
      error: {
        text: `${SYMBOLS.STATUS.ERROR}失败`,
      },
      default: {
        text: '复制',
      },
    }
    const { duration = 400, showPlaceholder = true, states = {} } = options || {}
    let customStates = states?.[status] ?? {}

    // 清空默认状态文本
    if (!showPlaceholder && status === 'default')
      customStates = { text: '' }

    const state = { ...defaultStates[status], ...customStates }

    // 根据更新类型执行不同操作
    if (updateType === CopyButtonUpdateType.TextContent) {
      $button.text(state.text).addClass(`copy-${status}`)
    }
    else if (updateType === CopyButtonUpdateType.DataAttribute) {
      $button.attr('data-title', state.text).addClass(`copy-${status}`)
    }
    else {
      throw new Error('无效的更新类型')
    }

    // 非默认状态时设置恢复定时器
    if (status !== 'default') {
      setTimeout(() => {
        this.updateCopyButtonStatus($button, updateType, 'default', options)
        $button.removeClass(`copy-${status}`)
      }, duration)
    }
  }

  /**
   * 执行复制操作
   */
  static async performCopy(content: string, $button: JQuery<HTMLElement>, updateType: CopyButtonUpdateType, options?: CopyButtonUpdateOptions): Promise<void> {
    try {
      if (!content)
        return

      // 复制到剪贴板
      await ClipboardUtils.copyToClipboard(content)
      // 更新按钮状态
      DomUtils.updateCopyButtonStatus($button, updateType, 'success', options)
    }
    catch (err) {
      logger.error('复制失败:', err)
      // 更新按钮状态
      DomUtils.updateCopyButtonStatus($button, updateType, 'error', options)
    }
  }

  /**
   * 创建 Shadow DOM 容器
   */
  static createShadowContainer(id: string): HTMLElement {
    const hostElement = document.createElement('div')
    hostElement.id = id
    hostElement.style.cssText = `all: initial;`

    // 创建 Shadow Root
    hostElement.attachShadow({ mode: 'open' })

    return hostElement
  }

  /**
   * 移除元素原生事件监听器（除了白名单内的）
   */
  static removeNativeEventListeners(selector: string | JQuery<HTMLElement>, whiteList?: string[]): void {
    try {
      const handler = (element: HTMLElement) => {
        if (whiteList && ElementUtils.isInSelectorList(element, whiteList))
          return

        element.replaceWith(element.cloneNode(true))
      }

      if (typeof selector === 'string') {
        document.querySelectorAll(selector).forEach((element) => {
          handler(element as HTMLElement)
        })
      }
      else if (ElementUtils.isJQueryObject(selector)) {
        selector.each((_, element) => {
          handler(element)
        })
      }
      else {
        logger.warn(`[${this.name}] 参数类型不支持，跳过移除原生事件监听器：${typeof selector}`)
        return
      }

      logger.debug(`[${this.name}] 已移除原生事件监听器：${selector}`)
    }
    catch (error) {
      logger.warn(`[${this.name}] 移除原生事件监听器失败: ${selector}`, error)
    }
  }
}
